﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

/* 
 * All functions related to player-specific moves are found here,
 * including movement, and hopefully abilties soon
 */

namespace Pulse
{
    class Player
    {
        public Sprite playerSprite;
        GraphicsDeviceManager playerGraphics;

        /* CONSTANTS */
        private float accelValue = 0.05f;
        private float decelValue = 0.08f;
        public int Health = 100;
        public Vector2 startPosition;
        public Vector2 drawOffset = new Vector2(-48, -68); 
        // Drawing sprite in such a way that the Position is represented by the character's feet
        
        // [Flags]
        public bool alive = true;
        public bool visible = true;
        public bool colliding = false;
        public bool isWalking = false;
        
        public Color color = Color.White;

        /* Values that track the player's variables in-game */
        public Rectangle hitbox;
        public Vector2 velocity;
        public float maxSpeed = 10.0f;
        public float maxSpeedFactor = 1.0f;
        public int playerHeight = 0; /* how high above the ground the player is */


        public Player(Vector2 positon)
        {
            startPosition = positon;
        }

        public void PlayerGraphics(Texture2D playerTexture, GraphicsDeviceManager graphic)
        {
            this.playerSprite = new Sprite(playerTexture);
            this.playerGraphics = graphic;
            this.Position = startPosition;
            this.hitbox = new Rectangle((int)startPosition.X, (int)startPosition.Y, 96, 96);
            // 96 and 96 are magic numbers, height and width of a typical single sprite.
            // Though they could be changed technically...
        }

        public bool isVisible
        {
            get { return visible; }
        }

        public Vector2 spriteCentre
        {
            get { return playerSprite.spriteCentre; }
            set { playerSprite.spriteCentre = value; }
        }

        // returns the coordinate of the top-left pixel of the sprite
        public Vector2 Position
        {
            get { return playerSprite.position; }
            set { playerSprite.position = value; }
        }


        public float Rotation
        {
            get { return playerSprite.Rotation; }
            set { playerSprite.Rotation = value; }
        }

        /* 
         * Based on player input, calculate the new velocities and position 
         */
        /// <summary>
        /// Move the player sprite based on user input
        /// </summary>
        public void UpdateMovement(GameTime gametime, KeyboardState ks)
        {
            /* Apply friction to player movement */
            velocity.X += ((0 * maxSpeed * maxSpeedFactor) - velocity.X) * decelValue;
            velocity.Y += ((0 * maxSpeed * maxSpeedFactor) - velocity.Y) * decelValue;

            /* "Sprint" function */
            if (ks.IsKeyDown(Keys.LeftShift))
                this.maxSpeedFactor = 1.5f;
            else this.maxSpeedFactor = 1.0f;

            /* Calculate player velocity based on keyboard input */
            if (ks.IsKeyDown(Keys.A))
            {
                this.isWalking = true;
                velocity.X += ((-1 * maxSpeed * maxSpeedFactor) - velocity.X) * accelValue;
            }
            else if (ks.IsKeyDown(Keys.D))
            {
                this.isWalking = true;
                velocity.X += ((1 * maxSpeed * maxSpeedFactor) - velocity.X) * accelValue;
            }
            else
            {
                this.isWalking = false;
            }

            if (ks.IsKeyDown(Keys.W))
            {
                this.isWalking = true;
                velocity.Y += ((-1 * maxSpeed * maxSpeedFactor) - velocity.Y) * accelValue;
            }
            else if (ks.IsKeyDown(Keys.S))
            {
                this.isWalking = true;
                velocity.Y += ((1 * maxSpeed * maxSpeedFactor) - velocity.Y) * accelValue;
            }
            else
            {
                this.isWalking = false;
            }

            playerSprite.Rotation = ((float)Math.Atan2((double)velocity.Y, (double)velocity.X));
        }

        /* 
         * Based on the player's current rotation, pick the correct frame to play
         */
        /// <summary>
        /// Change the current animation based on player's current movement
        /// </summary>
        public void UpdateWalkFrame()
        {
            string walker = "walk";
            string pauser = "pause";
            double angleSection = 360.0f / 8.0f;
            double currentAngle = MathHelper.ToDegrees(playerSprite.Rotation);
            int frame = (int)Math.Round(currentAngle / angleSection);

            /* 
             * When dealing with negative angles, offsetting is needed.
             * -180 represents going left,
             * -90 represents going straight up,
             * and negative numbers close to 0 go right.
             * 
             * The animation for going straight up yields a calculation of 
             * -90/(360/8) = -2.
             * 
             * The sprite animation for going up is located
             * 2 rows up from the bottom.
             */
            if (0 > frame)
            {
                // Example:
                // -2 = 8 + (-2)
                // 6
                frame = 8 + frame;
            }

            walker += Convert.ToString(frame);
            pauser += Convert.ToString(frame);

            /* 
             * 0.05f is magic number, the threshold value at 
             * which the player is considered still, 
             * ignoring the floating decimals 
             */
            if (Math.Abs(velocity.X) < 0.05 && Math.Abs(velocity.Y) < 0.05)
            {
                playerSprite.CurrentAnimation = pauser;
            }
            else if (playerSprite.CurrentAnimation != walker)
            {
                playerSprite.CurrentAnimation = walker;
            }
        }

        public void CollisionResolve()
        {
            if (colliding)
            {
                Health--;
                Health = (int)MathHelper.Clamp(Health, 0, 1000);
            }
        }

        public void Update(GameTime gametime, KeyboardState ks)
        {
            UpdateMovement(gametime, ks);
            UpdateWalkFrame();

            playerSprite.Update(gametime);
        }

        public void Draw(SpriteBatch spritebatch, int offsetX, int offsetY)
        {
            if (visible)
            {
                Vector2 heightOffset = new Vector2(offsetX, offsetY);

                spritebatch.Draw(playerSprite.spriteTexture,
                    (playerSprite.Position + playerSprite.spriteCentre + drawOffset + heightOffset),
                    playerSprite.CurrentFrameAnimation.getCurrentSourceRectangle,
                    playerSprite.color, 0.0f, playerSprite.spriteCentre,
                    playerSprite.scale, SpriteEffects.None, playerSprite.drawOrder);
                //playerSprite.Draw(spritebatch);
            }
        }
    }
}
